package com.spatio.serving.basic.service.template.core.component.redission;

import com.spatio.serving.spring.boot.starter.prometheus.op.impl.RedissonPrometheusOp;
import org.redisson.api.RLock;

import java.util.concurrent.TimeUnit;

public class DistributeLock {

    private static final String DISTRIBUTE_LOCK_FORMAT = "distribute:lock:{%s}";
    private static final String LOCK = "lock";
    private static final String TRY_LOCK = "try_lock";
    private static final String UN_LOCK = "un_lock";

    public static void lock(String distributeId) {
        RedissonPrometheusOp.op().qpsTotal(TRY_LOCK);
        long start = System.currentTimeMillis();
        RedissonHolder.getInstance().getLock(cacheKey(distributeId)).lock();
        RedissonPrometheusOp.op().latency(TRY_LOCK, (System.currentTimeMillis() - start), TimeUnit.MILLISECONDS);
    }

    public static void lock(String distributeId, long leaseTime, TimeUnit timeUnit) {
        RedissonPrometheusOp.op().qpsTotal(TRY_LOCK);
        long start = System.currentTimeMillis();
        RedissonHolder.getInstance().getLock(cacheKey(distributeId)).lock(leaseTime, timeUnit);
        RedissonPrometheusOp.op().latency(TRY_LOCK, (System.currentTimeMillis() - start), TimeUnit.MILLISECONDS);
    }

    public static boolean tryLock(String distributeId) {
        RedissonPrometheusOp.op().qpsTotal(TRY_LOCK);
        long start = System.currentTimeMillis();
        boolean flag = RedissonHolder.getInstance().getLock(cacheKey(distributeId)).tryLock();
        RedissonPrometheusOp.op().latency(TRY_LOCK, (System.currentTimeMillis() - start), TimeUnit.MILLISECONDS);
        return flag;
    }

    public static boolean tryLock(String distributeId, long waitTime, long leaseTime, TimeUnit timeUnit) {
        try {
            RedissonPrometheusOp.op().qpsTotal(TRY_LOCK);
            long start = System.currentTimeMillis();
            boolean flag = RedissonHolder.getInstance().getLock(cacheKey(distributeId)).tryLock(waitTime, leaseTime, timeUnit);
            RedissonPrometheusOp.op().latency(TRY_LOCK, (System.currentTimeMillis() - start), TimeUnit.MILLISECONDS);
            return flag;
        } catch (Exception e) {
            return false;
        }
    }

    public static boolean tryLock(String distributeId, long time, TimeUnit timeUnit) {
        try {
            RedissonPrometheusOp.op().qpsTotal(TRY_LOCK);
            long start = System.currentTimeMillis();
            boolean flag = RedissonHolder.getInstance().getLock(cacheKey(distributeId)).tryLock(time, timeUnit);
            RedissonPrometheusOp.op().latency(TRY_LOCK, (System.currentTimeMillis() - start), TimeUnit.MILLISECONDS);
            return flag;
        } catch (Exception e) {
            return false;
        }
    }

    public static RLock genMultiLock(String distributeId1, String distributeId2) {
        RedissonPrometheusOp.op().qpsTotal(TRY_LOCK);
        RLock lock1 = RedissonHolder.getInstance().getLock(cacheKey(distributeId1));
        RLock lock2 = RedissonHolder.getInstance().getLock(cacheKey(distributeId2));
        return RedissonHolder.getInstance().getMultiLock(lock1, lock2);
    }


    public static void unLock(String distributeId) {
        RedissonPrometheusOp.op().qpsTotal(UN_LOCK);
        long start = System.currentTimeMillis();
        RedissonHolder.getInstance().getLock(cacheKey(distributeId)).unlock();
        RedissonPrometheusOp.op().latency(UN_LOCK, (System.currentTimeMillis() - start), TimeUnit.MILLISECONDS);
    }

    public static void unLockSelfLock(String distributeId) {
        RedissonPrometheusOp.op().qpsTotal(UN_LOCK);
        long start = System.currentTimeMillis();
        RLock lock = RedissonHolder.getInstance().getLock(cacheKey(distributeId));
        if (lock.isHeldByCurrentThread()) {
            lock.unlock();
            RedissonPrometheusOp.op().latency(UN_LOCK, (System.currentTimeMillis() - start), TimeUnit.MILLISECONDS);
        }
    }

    private static String cacheKey(String type) {
        return String.format(DISTRIBUTE_LOCK_FORMAT, type);
    }

}